﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLNGP.Core.Fitness;
using SLNGP.Core.GeneticPrograms;
using SLNGP.Core.Individuals;
using System.Linq;
using SLNGP.Common.Extensions;
using SLNGP.Core.SecondaryStrategies;
using FitnessNumber = System.Int32;

namespace SLNGP.Core.GeneticOpearators
{
    /// <summary>
    /// Has adaptive fitness selection algoirthm. 
    /// Based on: http://en.wikipedia.org/wiki/Fitness_proportionate_selection
    /// </summary>
    public class RouletteWheelSelection : SelectionGeneticOpearator
    {
        public RouletteWheelSelection(int crossoverPercentage, int mutationPercentage, int carryUnchangedPercentage, int killPercentage)
            : base(crossoverPercentage, mutationPercentage, carryUnchangedPercentage, killPercentage)
        {
        }

        /// <summary>
        /// Collection of Individuals and size of roulette slot (effective fitness) 
        /// </summary>
        private Dictionary<IIndividual, FitnessNumber> Roulette = null;

        public override void Execute()
        {
            Roulette = new Dictionary<IIndividual, FitnessNumber>();
            InitializeRouletteWheel();

            foreach (var unselectedIndividual in Roulette)
                unselectedIndividual.Key.SelectionMarker = null;

            int numberOfIndividuals = LastGeneration.Count;
            PerformSelection(numberOfIndividuals, CrossoverPercentage, SelectionMarkers.Crossover);
            PerformSelection(numberOfIndividuals, MutationPercentage, SelectionMarkers.Mutation);
            PerformSelection(numberOfIndividuals, CarryUnchangedPercentage, SelectionMarkers.CarryToNextGenerationUnchanged);

            foreach (var unselectedIndividual in Roulette)
                unselectedIndividual.Key.SelectionMarker = SelectionMarkers.Kill;
        }

        private void PerformSelection(int numberOfIndividuals, int Percentage, SelectionMarkers selectionMarker)
        {
            int numberOfIndividualsToSelectForCrossover = MathHelper.GetPercentage(numberOfIndividuals, Percentage);
            for (int i = 0; i < numberOfIndividualsToSelectForCrossover; i++)
                SelectRandomIndividual().SelectionMarker = selectionMarker;
        }

        private IIndividual SelectRandomIndividual()
        {
            IIndividual ReturnValue = null;
            FitnessNumber fitnessSum = Roulette.Sum(p => p.Value);
            FitnessNumber rouletteResult = Parent.GetStrategy<IRandomProvider>().Random(0, fitnessSum);
            FitnessNumber currentSum = 0;

            foreach (var rouletteWheelSlot in Roulette)
            {
                if (rouletteResult >= currentSum && rouletteResult <= currentSum + rouletteWheelSlot.Value)
                {
                    if (rouletteWheelSlot.Key.SelectionMarker == null)
                    {
                        ReturnValue = rouletteWheelSlot.Key;
                    }
                }
                currentSum += rouletteWheelSlot.Value;
            }
            Roulette.Remove(ReturnValue);
            return ReturnValue;
        }


        private void InitializeRouletteWheel()
        {
            Parent.FitnessFunction.InitializeForEffectiveFitness(LastGeneration);
            foreach (IIndividual individualToSetSelectionMarkersOn in LastGeneration)
            {
                Roulette[individualToSetSelectionMarkersOn] =
                    Parent.FitnessFunction.GetEffectiveFitness(
                        individualToSetSelectionMarkersOn.Fitness.As<FitnessNumber>(), LastGeneration);
            }
        }
    }
}
